package queue

import (
	"context"
	"sync"
)

// 自己实现的 cond 来控制阻塞超时的并发队列，还使用了循环数组来存储数据
type ConcurrentBlockQueueV3[T any] struct {
	mu          *sync.Mutex
	data        []T
	dequeSignal *Cond
	enqueSignal *Cond
	maxSize     int
	head        int
	tail        int
	count       int
	zero        T
}

func NewConcurrentBlockQueueV3[T any](maxSize int) *ConcurrentBlockQueueV3[T] {
	l := &sync.Mutex{}
	return &ConcurrentBlockQueueV3[T]{
		mu:          l,
		data:        make([]T, maxSize),
		maxSize:     maxSize,
		dequeSignal: NewCond(l),
		enqueSignal: NewCond(l),
	}
}

func (c *ConcurrentBlockQueueV3[T]) EnQueue(ctx context.Context, data T) error {
	c.mu.Lock()
	for c.isFull() {
		err := c.dequeSignal.WaitTimeout(ctx)
		// 超时不会拿到锁，也不需要解锁
		if err != nil {
			return err
		}
	}

	// c.data = append(c.data, data)
	c.data[c.tail] = data
	c.tail++
	c.count++
	if c.tail == c.maxSize {
		c.tail = 0
	}
	c.enqueSignal.Broadcast()
	c.mu.Unlock()

	return nil
}
func (c *ConcurrentBlockQueueV3[T]) DeQueue(ctx context.Context) (T, error) {
	c.mu.Lock()
	for c.isEmpty() {
		var t T
		err := c.enqueSignal.WaitTimeout(ctx)
		// 超时不会拿到锁，也不需要解锁
		if err != nil {
			return t, err
		}
	}
	// data := c.data[0]
	// c.data = c.data[1:]
	data := c.data[c.head]
	// 填充零值，防止 gc 回收漏掉
	c.data[c.head] = c.zero
	c.head++
	c.count--
	if c.head == c.maxSize {
		c.head = 0
	}
	c.dequeSignal.Broadcast()
	c.mu.Unlock()
	return data, nil
}

func (c *ConcurrentBlockQueueV3[T]) IsFull() bool {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.isFull()
}
func (c *ConcurrentBlockQueueV3[T]) isFull() bool {
	// return len(c.data) == c.maxSize
	return c.count == c.maxSize
}
func (c *ConcurrentBlockQueueV3[T]) IsEmpty() bool {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.isEmpty()
}
func (c *ConcurrentBlockQueueV3[T]) isEmpty() bool {
	// return len(c.data) == 0
	return c.count == 0
}
func (c *ConcurrentBlockQueueV3[T]) Len() uint64 {
	c.mu.Lock()
	defer c.mu.Unlock()
	// return uint64(len(c.data))
	return uint64(c.count)
}
